package com.ztesoft.zsmart.zcm.dialing.util.scriptUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.ztesoft.zsmart.core.log.ZSmartLogger;
import org.apache.commons.io.IOUtils;

/**
 * 通过系统命令执行的类 提供了相关的方法 1. 执行准备 将脚本内容输出到文件中，并返回文件名 会自动先判断文件系统是否存在相关文件，如果存在则不需要写，如果不存在则写入
 * 写入文件的时候会先写入，再读取并比对，以确保写入成功，否则返回错误 2. 执行 根据传入的命令数组，调用一个线程执行，等待返回结果并进行解析
 * 
 * @author
 */
public final class ShellExecutor {

    private static ZSmartLogger logger = ZSmartLogger.getLogger(ShellExecutor.class);

    /**
     * ShellExecutor <br>
     */
    private ShellExecutor() {
        // TODO Auto-generated constructor stub
    }

    /**
     * 直接执行命令并返回结果
     * 
     * @return map {"state":0|1,"normalInfo":,"errorInfo":,"waitForFlag":,"procExitValue":}
     */
    public static Map<String, String> execute(List<String> cmdList, Map<String, String> envParam) {
        return execute(cmdList, envParam, null);
    }

    public static Map<String, String> execute(String cmd, Map<String, String> envParam, String workPath) {
        List<String> cmdList = new ArrayList<String>();
        cmdList.add(cmd);
        return execute(cmdList, envParam, workPath);
    }

    /**
     * 直接执行命令并返回结果<br>
     * <br>
     * wangyumu 2013/4/25 发现问题: 当使用脚本读取一个较大的文件的时候，造成SHELL执行的子进程挂死<br>
     * 分析原因：由于一个系统的STDOUT和STDERR容量有限，当读取较大文件的时候，STDOUT已经满了<br>
     * 如果先尝试读取STDERR，会造成进程无法写STDOUT，而形成假死<br>
     * 第一次方法： 因此利用ProcessBuilder将STDERR重定向到STDOUT，然后只读取STDOUT来解决这个问题<br>
     * 同时根据procExitValue来判断读取的信息是错误信息，还是有效信息。 改良方式: 将读取错误流放入单独的一个线程来执行，后续需要优化一下线程的创建<br>
     * 
     * @return map {"normalInfo":,"errorInfo":,"waitForFlag":}
     */
    public static Map<String, String> execute(List<String> cmdList, Map<String, String> envParam, String workDir) {
        StringBuffer rsltStrBuffer = new StringBuffer(); // 保存返回的结果信息
        StringBuffer errStrBuffer = new StringBuffer(); // 保存返回的错误信息
        int waitForFlag = -1; // 等待SHELL线程完成的标识
        StreamReader streamReader = null;
        Process proc = null;
        Map<String, String> result = new HashMap<String, String>();

        try {
            logger.debug("Start Command [{}],EnvParam [{}],workDir [{}]", cmdList, envParam, workDir);
            // 启动命令
            proc = startCommand(cmdList, envParam, "".equals(workDir) ? null : workDir);

            // 读取异常信息
            streamReader = new StreamReader(proc.getErrorStream(), errStrBuffer);
            streamReader.start();
            Thread.sleep(100);

            rsltStrBuffer.append(IOUtils.toString(new InputStreamReader(proc.getInputStream(), "utf-8")));

            // 等待命令执行完成，并返回命令执行状态
            waitForFlag = proc.waitFor();
            // procExitValue = proc.exitValue();
            Thread.sleep(100);

        }
        catch (Exception e) {
            logger.error("Command [" + cmdList.toString() + "] Execute  Error: ", e);
            errStrBuffer.append("Command " + cmdList.toString() + " Execute  Error: ");
            errStrBuffer.append(e.getMessage());
        }
        finally {
            try {
                if (proc != null) {
                    proc.destroy();
                }
            }
            finally {
                if (streamReader != null) {
                    streamReader.interrupt();
                }
            }
        }

        // result.put("state", String.valueOf(waitForFlag == 0 && procExitValue == 0 ? 0 : 1));
        result.put("waitForFlag", String.valueOf(waitForFlag));
        // result.put("procExitValue", String.valueOf(procExitValue));
        result.put("normalInfo", rsltStrBuffer.toString());
        result.put("errorInfo", errStrBuffer.toString());

        logger.debug("Return Result {}", result);

        return result;
    }

    /**
     * Description: <br>
     *
     * @return Process <br>
     */
    private static Process startCommand(List<String> cmdList, Map<String, String> envParam, String workDir)
        throws IOException {
        ProcessBuilder pb = new ProcessBuilder(cmdList);
        if (workDir != null) {
            pb.directory(new File(workDir));
        }
        if (envParam != null) {
            /**
             * 解密参数
             */
            Iterator<Map.Entry<String, String>> itStr = envParam.entrySet().iterator();
            while (itStr.hasNext()) {
                Map.Entry<String, String> entry = itStr.next();
                if (entry.getKey() != null && entry.getKey().trim().length() > 1) {
                    String name = entry.getKey().trim();
                    String value = entry.getValue();
                    pb.environment().put(name, value);
                }
            }
        }
        return pb.start();
    }

}
